package com.google.code.chaotic.server;

import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;

import com.google.code.chaotic.ConversionUtil;
import com.google.code.chaotic.client.LoginDto;
import com.google.code.chaotic.dto.UserDto;
import com.google.code.chaotic.model.Admin;
import com.google.code.chaotic.model.Commuter;
import com.google.code.chaotic.model.CommutingHabit;
import com.google.code.chaotic.model.User;
import com.google.code.chaotic.rpc.AuthSvc;
import com.google.code.chaotic.shared.AuthException;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;

//

public class AuthSvcImpl extends RemoteServiceServlet implements AuthSvc {

	private static Logger logger = Logger.getLogger( //
			AuthSvcImpl.class.getName());

	private static final String ERR_MSG = "Invalid username/password combination";

	//

	@Override
	public LoginDto login(String username, String password)
			throws AuthException {

		// EntityManager em = EmFactory.get().createEntityManager();
		PersistenceManager pm = PmFactory.get().getPersistenceManager();
		// String jqlStmt = "SELECT u FROM User u " + "WHERE u.username = ?1";
		// Query query = em.createQuery(jqlStmt);
		// query.setParameter(1, username);

		// // Just for the sake of development time's evaluations
		// String id =
		// "agphbnRpLWNoYW9zciQLEghDb21tdXRlchimBgwLEg5Db21tdXRpbmdIYWJpdBinBgw";
		// CommutingHabit obj = pm.getObjectById(CommutingHabit.class, id);
		// System.out.printf(">>>>>>>>>> Here it is: %s \n", obj.getCommuter());

		// TODO: Manually keeping track of subclasses
		Query commuterQuery = pm.newQuery(Commuter.class, "username == u1");
		Query adminQuery = pm.newQuery(Admin.class, "username == u1");
		commuterQuery.declareParameters("String u1");
		adminQuery.declareParameters("String u1");
		try {
			// User user = (User) query.getSingleResult();
			List users = new LinkedList();
			users.addAll((List) commuterQuery.execute(username));
			users.addAll((List) adminQuery.execute(username));
			if (users.size() < 1) {
				AuthException ex = new AuthException(ERR_MSG);
				logger.log(Level.SEVERE, ex.toString(), ex);
				throw ex;
			}
			User user = (User) users.get(0);

			// TODO: Hashing must be enabled for the roll-out
			// BCrypt.checkpw(password, foundUser.getPasswordMd5());
			if (!password.equals(user.getPasswordMd5())) {
				AuthException ex = new AuthException(ERR_MSG);
				logger.log(Level.SEVERE, ex.toString(), ex);
				throw ex;
			}
			// switch (user.getUserType()) {
			// case ADMIN:
			// user = em.find(Admin.class, user.getId());
			// break;
			// case COMMUTER:
			// user = em.find(Commuter.class, user.getId());
			// break;
			// }
			String sessionId = generateSessionId();
			LoginDto loginDto = createLoginDto(sessionId, user);
			ServerSessionManager.getInstance().addSession(sessionId, user);
			return loginDto;

			// } catch (NoResultException e) {
			// logger.log(Level.SEVERE, e.toString(), e);
			// throw new AuthException(ERR_MSG);
			//
			// } catch (NonUniqueResultException e) {
			// logger.log(Level.SEVERE, e.toString(), e);
			// throw new AuthException(ERR_MSG);

		} finally {
			// em.close();
			pm.close();
		}
	}

	@Override
	public LoginDto getLoginDto(String sessionId) throws AuthException {
		User user = ServerSessionManager.getInstance().getUser(sessionId);
		if (user == null)
			throw new AuthException("Session expired.");

		return createLoginDto(sessionId, user);
	}

	@Override
	public void logout(String sessionId) {
		ServerSessionManager.getInstance().removeSession(sessionId);
	}

	private String generateSessionId() {
		UUID uuid = UUID.randomUUID();
		return Long.toHexString(uuid.getLeastSignificantBits())
				+ Long.toHexString(uuid.getMostSignificantBits());
	}

	private LoginDto createLoginDto(String sessionId, User user) {
		LoginDto loginDto = new LoginDto();
		loginDto.setSessionId(sessionId);

		UserDto userDto = ConversionUtil.entityToDto(user);
		userDto.setPasswordMd5(null);
		loginDto.setUserNoPass(userDto);
		return loginDto;
	}
}
